// Copyright (c) 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/**
*@describe:
*@author wfl19/Kristas
*@date 2021/10/30
 */

package update_mapper

import (
	"fmt"
	"gitee.com/kristas/booting-go/framework/common/util/lang"
	"gitee.com/kristas/booting-go/framework/data/tk/example"
	"reflect"
	"strings"
	"time"
)

type UpdateMapper struct {
	table string
}

func NewUpdateMapper(table string) *UpdateMapper {
	return &UpdateMapper{table}
}

func (s *UpdateMapper) UpdateByExample(example example.Example, value interface{}) error {
	fields := findValidField(value)
	var (
		props  []string
		values []interface{}
	)
	for name, val := range fields {
		props = append(props, fmt.Sprintf("`%s` = %s", name, fmtValue(val)))
		values = append(values, val)
	}
	var sql = fmt.Sprintf("%s `%s` %s %s ", KeywordUpdate, s.table, KeywordSet, strings.Join(props, ", "))
	explain, vals := example.Explain()
	values = append(values, vals...)
	sql += explain
	fmt.Println(sql, values)
	return nil
}

func findValidField(o interface{}) map[string]interface{} {
	t := reflect.TypeOf(o)
	v := reflect.ValueOf(o)
	result := make(map[string]interface{})
	switch t.Kind() {
	case reflect.Struct:
		for i := 0; i < t.NumField(); i++ {
			field := t.Field(i)
			val := v.Field(i)
			if !isNil(field, val) {
				result[fmtFieldName(field)] = val.Interface()
			}
		}
	case reflect.Ptr:
	case reflect.Map:

	}
	return result
}

func isNil(field reflect.StructField, val reflect.Value) bool {
	switch field.Type.Kind() {
	case reflect.Ptr, reflect.Slice, reflect.Map:
		return val.IsNil()
	default:
		return val.IsZero()
	}
}

func fmtFieldName(field reflect.StructField) string {
	if tag, ok := field.Tag.Lookup("tk"); ok {
		return tag
	}
	if tag, ok := field.Tag.Lookup("json"); ok {
		return tag
	}
	return lang.NewString(field.Name).SnakeCase().String()
}

func fmtValue(value interface{}) string {
	var fmtVal string
	switch value.(type) {
	case string:
		fmtVal = PlaceholderString
	case time.Time:
		fmtVal = PlaceholderString
	default:
		fmtVal = PlaceholderNumeric
	}
	return fmtVal
}
